package com.b2c.aspect;

import com.b2c.annotation.ApiLimit;
import exception.ApiLimitException;
import com.b2c.util.IpUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RList;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

import static content.common.DateFormat.FORMAT_PATTERN_SECODE;
import static content.common.ExceptionMessage.API_LIMIT;
import static content.common.RedisCommon.API_LIMIT_KEY_NAME_PREFIX;


@Aspect
@Component
public class ApiLimitAdvice {
    @Autowired
    private RedissonClient redissonClient;

    //匹配带有@ApiLimit注解的方法
    @Pointcut("@annotation(com.b2c.annotation.ApiLimit)")
    public void pointCut() {
    }

    @Before(value = "pointCut()", argNames = "pjp")
    public void around(JoinPoint pjp) throws Throwable {
        //获取方法签名
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        ApiLimit apiLimit = signature.getMethod().getAnnotation(ApiLimit.class);
        //获取注解属性
        int limitCount = apiLimit.limitCount();
        long time = apiLimit.time();
        ChronoUnit timeunit = apiLimit.timeunit();

        //获取httpServletRequest
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest httpServletRequest = attributes.getRequest();


        //获取uri和ip地址
        String uri = httpServletRequest.getRequestURI();
        String ipAddress = IpUtil.getIpAddress(httpServletRequest);


        //redis的key
        String key = API_LIMIT_KEY_NAME_PREFIX + ipAddress + uri;
        RList<String> rList = redissonClient.getList(key);
        //达到限流上限
        if (rList.size() >= limitCount) {
            throw new ApiLimitException(API_LIMIT);
        }
        //将访问存入缓存
        rList.add(LocalDateTime.now().format(DateTimeFormatter.ofPattern(FORMAT_PATTERN_SECODE)));
        //第一次进来设置超时时间
        if (rList.size() == 1) {
            rList.expire(Duration.of(time, timeunit));
        }
    }


}
